{
 "metadata": {
  "signature": "sha256:3a3118001a4a2729653d99e5fb42cd971944ff4c164098754a19750bc79434c8"
 },
 "nbformat": 3,
 "nbformat_minor": 0,
 "worksheets": [
  {
   "cells": [
    {
     "cell_type": "markdown",
     "metadata": {},
     "source": [
      "Embedding in Traits GUI\n",
      "======================================================================\n",
      "\n",
      "Embedding a Matplotlib Figure in a Traits App\n",
      "---------------------------------------------\n",
      "\n",
      "Traits, part of the [Enthought Tools Suit](http://code.enthought.com/),\n",
      "provides a great framework for creating GUI Apps without a lot of the\n",
      "normal boilerplate required to connect the UI the rest of the\n",
      "application logic. A brief introduction to Traits can be found\n",
      "[here](http://wiki.scipy.org/TraitsUI). Although ETS comes with it's own traits-aware plotting\n",
      "framework (Chaco), if you already know matplotlib it is just as easy to\n",
      "embed this instead. The advantages of Chaco (IMHO) are its interactive\n",
      "\"tools\", an (in development) OpenGL rendering backend and an\n",
      "easy-to-understand codebase. However, matplotlib has more and better\n",
      "documentation and better defaults; it just works. The key to getting\n",
      "TraitsUI and matplotlib to play nice is to use the mpl object-oriented\n",
      "API, rather than pylab / pyplot. This recipe requires the following\n",
      "packages:\n",
      "\n",
      "* numpy\n",
      "* wxPython\n",
      "* matplotlib\n",
      "* Traits > 3.0\n",
      "* TraitsGUI > 3.0\n",
      "* TraitsBackendWX > 3.0\n",
      "\n",
      "For this example, we will display a function (y, a sine wave) of one variable (x, a numpy ndarray) and one parameter (scale, a float value with bounds). We want to be able to vary the parameter from the UI and see the resulting changes to y in a plot window. Here's what the final result looks like: ![](files/EmbeddingInTraitsGUI/mpl_in_traits_view.png) The TraitsUI \"!CustomEditor\" can be used to display any wxPython window as the editor for the object. You simply pass the !CustomEditor a callable which, when called, returns the wxPython window you want to display. In this case, our !MakePlot() function returns a wxPanel containing the mpl !FigureCanvas and Navigation toolbar. This example exploits a few of Traits' features. We use \"dynamic initialisation\" to create the Axes and Line2D objects on demand (using the _xxx_default methods).  We use Traits \"notification\", to call update_line(...) whenever the x- or y-data is changed. Further, the y-data is declared as a Property trait which depends on both the 'scale' parameter and the x-data. 'y' is then recalculated on demand, whenever either 'scale' or 'x' change. The 'cached_property' decorator prevents recalculation of y if it's dependancies `*`are`\n",
      ")# `not`*` modified.`\n",
      "\n",
      "Finally, there's a bit of wx-magic in the redraw() method to limit the\n",
      "redraw rate by delaying the actual drawing by 50ms. This uses the\n",
      "wx.!CallLater class. This prevents excessive redrawing as the slider is\n",
      "dragged, keeping the UI from lagging. [Here's](files/attachments/EmbeddingInTraitsGUI/mpl_editor.py)\n",
      "the full listing:"
     ]
    },
    {
     "cell_type": "code",
     "collapsed": false,
     "input": [
      "#!python\n",
      "\"\"\"\n",
      "A simple demonstration of embedding a matplotlib plot window in\n",
      "a traits-application. The CustomEditor allow any wxPython window\n",
      "to be used as an editor. The demo also illustrates Property traits,\n",
      "which provide nice dependency-handling and dynamic initialisation, using\n",
      "the _xxx_default(...) method.\n",
      "\"\"\"\n",
      "from enthought.traits.api import HasTraits, Instance, Range,\\\n",
      "                                Array, on_trait_change, Property,\\\n",
      "                                cached_property, Bool\n",
      "from enthought.traits.ui.api import View, Item\n",
      "from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg\n",
      "from matplotlib.backends.backend_wx import NavigationToolbar2Wx\n",
      "from matplotlib.figure import Figure\n",
      "from matplotlib.axes import Axes\n",
      "from matplotlib.lines import Line2D\n",
      "from enthought.traits.ui.api import CustomEditor\n",
      "import wx\n",
      "import numpy\n",
      "def MakePlot(parent, editor):\n",
      "    \"\"\"\n",
      "    Builds the Canvas window for displaying the mpl-figure\n",
      "    \"\"\"\n",
      "    fig = editor.object.figure\n",
      "    panel = wx.Panel(parent, -1)\n",
      "    canvas = FigureCanvasWxAgg(panel, -1, fig)\n",
      "    toolbar = NavigationToolbar2Wx(canvas)\n",
      "    toolbar.Realize()\n",
      "    sizer = wx.BoxSizer(wx.VERTICAL)\n",
      "    sizer.Add(canvas,1,wx.EXPAND|wx.ALL,1)\n",
      "    sizer.Add(toolbar,0,wx.EXPAND|wx.ALL,1)\n",
      "    panel.SetSizer(sizer)\n",
      "    return panel\n",
      "class PlotModel(HasTraits):\n",
      "    \"\"\"A Model for displaying a matplotlib figure\"\"\"\n",
      "    #we need instances of a Figure, a Axes and a Line2D\n",
      "    figure = Instance(Figure, ())\n",
      "    axes = Instance(Axes)\n",
      "    line = Instance(Line2D)\n",
      "    _draw_pending = Bool(False) #a flag to throttle the redraw rate\n",
      "    #a variable paremeter\n",
      "    scale = Range(0.1,10.0)\n",
      "    #an independent variable\n",
      "    x = Array(value=numpy.linspace(-5,5,512))\n",
      "    #a dependent variable\n",
      "    y = Property(Array, depends_on=['scale','x'])\n",
      "    traits_view = View(\n",
      "                    Item('figure',\n",
      "                         editor=CustomEditor(MakePlot),\n",
      "                         resizable=True),\n",
      "                    Item('scale'),\n",
      "                    resizable=True\n",
      "                    )\n",
      "    def _axes_default(self):\n",
      "        return self.figure.add_subplot(111)\n",
      "    def _line_default(self):\n",
      "        return self.axes.plot(self.x, self.y)[0]\n",
      "    @cached_property\n",
      "    def _get_y(self):\n",
      "        return numpy.sin(self.scale * self.x)\n",
      "    @on_trait_change(\"x, y\")\n",
      "    def update_line(self, obj, name, val):\n",
      "        attr = {'x': \"set_xdata\", 'y': \"set_ydata\"}[name]\n",
      "        getattr(self.line, attr)(val)\n",
      "        self.redraw()\n",
      "    def redraw(self):\n",
      "        if self._draw_pending:\n",
      "            return\n",
      "        canvas = self.figure.canvas\n",
      "        if canvas is None:\n",
      "            return\n",
      "        def _draw():\n",
      "            canvas.draw()\n",
      "            self._draw_pending = False\n",
      "        wx.CallLater(50, _draw).Start()\n",
      "        self._draw_pending = True\n",
      "if __name__==\"__main__\":\n",
      "    model = PlotModel(scale=2.0)\n",
      "    model.configure_traits()"
     ],
     "language": "python",
     "metadata": {},
     "outputs": []
    }
   ],
   "metadata": {}
  }
 ]
}